package com.ldl.exam.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.ldl.exam.constant.Constants;
import com.ldl.exam.dao.*;
import com.ldl.exam.entity.*;
import com.ldl.exam.exception.ExamException;
import com.ldl.exam.service.ExamPaperService;
import com.ldl.exam.util.CommonUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author liudaolin
 * @version 1.0
 * @date 2022/10/02 21:32:09
 */
@Transactional(rollbackFor = Exception.class)
@Service
public class ExamPaperServiceImpl implements ExamPaperService {
    @Autowired
    private ExamPaperMapper examPaperMapper;
    @Autowired
    private ExamPaperDetailMapper examPaperDetailMapper;
    @Autowired
    private QuestionMapper questionMapper;
    @Autowired
    private StuExamPaperMapper stuExamPaperMapper;
    @Autowired
    private StuExamAnswerMapper stuExamAnswerMapper;
    @Autowired
    private StuMapper stuMapper;

    @Override
    public PageInfo<StuExamPaper> findExamsByStuIdAndPage(Integer stuId, int pageNum, int pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        StuExamPaperExample example = new StuExamPaperExample();
        example.or()
                .andStuIdEqualTo(stuId)
                .andScoreRankNotEqualTo(Constants.STU_EXAM_PAPER_INIT);
        example.setOrderByClause("create_date desc");// 时间升序
        List<StuExamPaper> list = stuExamPaperMapper.selectByExample(example);
        return new PageInfo<StuExamPaper>(list, 5);
    }

    @Override
    public StuExamPaper previewExamByStuIdAndExamId(Integer stuId, Integer examPaperId) {
        StuExamPaperExample example1 = new StuExamPaperExample();
        example1.or()
                .andStuIdEqualTo(stuId).andExamPaperIdEqualTo(examPaperId);
        List<StuExamPaper> stuExamPapers = stuExamPaperMapper.selectByExample(example1);
        if(stuExamPapers.isEmpty()) {
            throw new ExamException("未参加此考试");
        }
        StuExamPaper stuExamPaper = stuExamPapers.get(0);

        Stu stu = stuMapper.selectByPrimaryKey(stuId);
        stuExamPaper.setStu(stu);

        // 查询试卷题目
        List<QuestionWithBLOBs> questions = questionMapper.selectQuestionsHasAnswerByExamPaperId(examPaperId, null);
        stuExamPaper.setQuestions(questions);

        // 查询学生的答案
        StuExamAnswerExample example2 = new StuExamAnswerExample();
        example2.or()
                .andExamPaperIdEqualTo(examPaperId)
                .andStuIdEqualTo(stuId);
        List<StuExamAnswer> stuAnswers = stuExamAnswerMapper.selectByExampleWithBLOBs(example2);
        stuExamPaper.setStuExamAnswers(stuAnswers);

        return stuExamPaper;
    }

    @Override
    public String createJavaExam(String name, String language, int choiceCount, int shortCount, int programmingCount) {
        Date d = new Date();
        String str = CommonUtil.dateToString(d, "yyyyMMddHHmmss");
        String code = str + CommonUtil.random(4);

        ExamPaperWithBLOBs examPaper = new ExamPaperWithBLOBs();
        examPaper.setCode(code);
        examPaper.setName(name);
        examPaper.setCount(Constants.DEFAULT_VIOLATIONS_COUNT);
        examPaper.setCreateDate(d);
        examPaper.setStatus(Constants.EXAM_CREATED);

        // TODO: 抽取题目, 添加到 t_exam_paper_detail 表
        Random r = CommonUtil.random;
        // 选择题
        List<QuestionWithBLOBs> choiceList = questionMapper.selectQuestionsByTypeAndLanguage(Constants.CHOICE_QUESTION_TYPE, language);
        int choiceNum = choiceList.size();
        if(choiceNum < choiceCount) {
            throw new ExamException("目前最多设置["+ choiceNum +"]道选择题");
        }
        Set<Integer> choiceSet = new HashSet<>();
        while(choiceSet.size() < choiceCount) {
            int index = r.nextInt(choiceNum);
            choiceSet.add( choiceList.get(index).getId() );
        }
        // 简答题
        List<QuestionWithBLOBs> shortList = questionMapper.selectQuestionsByTypeAndLanguage(Constants.SHORT_QUESTION_TYPE, language);
        int shortNum = shortList.size();
        if(shortNum < shortCount) {
            throw new ExamException("目前最多设置["+ shortNum +"]道简答题");
        }
        Set<Integer> shortSet = new HashSet<>();
        while(shortSet.size() < shortCount) {
            int index = r.nextInt(shortNum);
            shortSet.add( shortList.get(index).getId() );
        }
        // 编程题
        List<QuestionWithBLOBs> programmingList = questionMapper.selectQuestionsByTypeAndLanguage(Constants.PROGRAMMING_QUESTION_TYPE, language);
        int programmingNum = programmingList.size();
        if(programmingNum < programmingCount) {
            throw new ExamException("目前最多设置["+ programmingNum +"]道编程题");
        }
        Set<Integer> programmingSet = new HashSet<>();
        while(programmingSet.size() < programmingCount) {
            int index = r.nextInt(programmingNum);
            programmingSet.add( programmingList.get(index).getId() );
        }

        // 创建试卷
        examPaperMapper.insertSelective(examPaper);
        Integer examPaperId = examPaper.getId();

        // 添加选择题
        for (Integer questionId : choiceSet) {
            examPaperDetailMapper.insertExamPaperIdAndQuestionId(examPaperId, questionId);
        }
        // 添加简答题
        for (Integer questionId : shortSet) {
            examPaperDetailMapper.insertExamPaperIdAndQuestionId(examPaperId, questionId);
        }
        // 添加编程题
        for (Integer questionId : programmingSet) {
            examPaperDetailMapper.insertExamPaperIdAndQuestionId(examPaperId, questionId);
        }
        return code;
    }

    @Override
    public String createExamPaper(String examName) {
        Date d = new Date();
        StringBuffer codeBuffer = new StringBuffer()
                .append(CommonUtil.dateToString(d, "yyyyMMdd"))
                .append(CommonUtil.random(4));

        ExamPaperWithBLOBs examPaper = new ExamPaperWithBLOBs();
        examPaper.setCode(codeBuffer.toString());
        examPaper.setName(examName);
        examPaper.setCount(Constants.DEFAULT_VIOLATIONS_COUNT);
        examPaper.setCreateDate(d);
        examPaper.setStatus(Constants.EXAM_CREATED);

        // 创建试卷
        examPaperMapper.insertSelective(examPaper);
        Integer examPaperId = examPaper.getId();
        codeBuffer.append(examPaperId);

        String code = codeBuffer.toString();

        ExamPaperWithBLOBs examPaper2 = new ExamPaperWithBLOBs();
        examPaper2.setId(examPaperId);
        examPaper2.setCode(code);
        // 设置 code
        examPaperMapper.updateByPrimaryKeySelective(examPaper2);

        return code;
    }

    @Override
    public void startExam(Integer examPaperId) throws ExamException{
        ExamPaperExample examPaperExample = new ExamPaperExample();
        examPaperExample.or().andIdEqualTo(examPaperId);
        ExamPaperWithBLOBs ep = examPaperMapper.selectByPrimaryKey(examPaperId);
//        List<ExamPaper> list = examPaperMapper.selectByExample(examPaperExample);
        if(null == ep){
            throw new ExamException("不存在编号为["+examPaperId+"]的考试");
        }
//        ExamPaper ep = list.get(0);
        switch (ep.getStatus()) {
            case Constants.EXAM_CREATED:
                int count = examPaperDetailMapper.selectQuestionsCountByExamPaperId(examPaperId);
                if (count < 1) {
                    throw new ExamException("此试卷还没有添加题目");
                }
                // 可以开始考试
                break;
            case Constants.EXAM_DOING:
                throw new ExamException("已经开始考试");
            case Constants.EXAM_FINISHED:
            case Constants.EXAM_RANKED:
                throw new ExamException("考试已结束");
            case Constants.EXAM_DELETED:
                throw new ExamException("该考试已被删除");
        }

        ExamPaperWithBLOBs examPaper = new ExamPaperWithBLOBs();
        examPaper.setId(examPaperId);
        examPaper.setStatus(Constants.EXAM_DOING);// 考试中
        int update = examPaperMapper.updateByPrimaryKeySelective(examPaper);
        if(update < 1) {
            throw new ExamException("不存在编号为["+examPaperId+"]的考试");
        }
    }

    @Override
    public ExamPaper findExam(String code) throws ExamException {
        // 查询试卷
        ExamPaperExample example = new ExamPaperExample();
        example.or().andCodeEqualTo(code);
        List<ExamPaper> examPapers = examPaperMapper.selectByExample(example);
        if(examPapers.isEmpty()) {
            throw new ExamException("考试码输入错误");
        }
        ExamPaper examPaper = examPapers.get(0);
        Integer status = examPaper.getStatus();
        switch (status) {
            case Constants.EXAM_CREATED:
                throw new ExamException("还未开始考试");
            case Constants.EXAM_DOING:
                // 可以开始考试
                break;
            case Constants.EXAM_FINISHED:
                throw new ExamException("本次考试已结束");
            case Constants.EXAM_DELETED:
                throw new ExamException("该考试场次不存在");
            default:
                throw new ExamException("该考试场次状态异常");
        }

        // 查询试卷题目
        List<QuestionWithBLOBs> questions = questionMapper.selectQuestionsNoAnswerByExamPaperId(examPaper.getId(), null);
        examPaper.setQuestions(questions);
        return examPaper;
    }

    @Override
    public void checkExamCode(String code) throws ExamException {
        // 查询试卷
        ExamPaperExample example = new ExamPaperExample();
        example.or().andCodeEqualTo(code);
        List<ExamPaper> examPapers = examPaperMapper.selectByExample(example);
        if(examPapers.isEmpty()) {
            throw new ExamException("考试码输入错误");
        }
        ExamPaper examPaper = examPapers.get(0);
        Integer status = examPaper.getStatus();
        switch (status) {
            case Constants.EXAM_CREATED:
                throw new ExamException("还未开始考试");
            case Constants.EXAM_DOING:
                // 可以开始考试
                break;
            case Constants.EXAM_FINISHED:
                throw new ExamException("本次考试已结束");
            case Constants.EXAM_DELETED:
                throw new ExamException("该考试场次不存在");
            default:
                throw new ExamException("该考试场次状态异常");
        }
    }

    @Override
    public void addStuAnswers(Integer examPaperId, Integer stuId, Integer count,List<StuExamAnswer> answers) throws ExamException {
        // 考试结束也不能交卷
        ExamPaperWithBLOBs examPaper = examPaperMapper.selectByPrimaryKey(examPaperId);
        if(null == examPaper) {
            throw new ExamException("不存在此考试");
        }
        if(!examPaper.getStatus().equals(Constants.EXAM_DOING)) {
            throw new ExamException("考试已结束, 无法交卷");
        }

        // 查询该学生是否已提交过本次考试的答案, 不能重复提交
        StuExamPaperExample example = new StuExamPaperExample();
        example.or().andStuIdEqualTo(stuId)
                .andExamPaperIdEqualTo(examPaperId);
        List<StuExamPaper> list = stuExamPaperMapper.selectByExample(example);
        if(!list.isEmpty()) {
            throw new ExamException("已交卷, 不能重复交卷");
        }

        // 添加 t_stu_exam_paper
        StuExamPaper stuExamPaper = new StuExamPaper();
        stuExamPaper.setStuId(stuId);
        stuExamPaper.setExamPaperId(examPaperId);
        stuExamPaper.setExamName(examPaper.getName());
        stuExamPaper.setTotalScore(0);
        stuExamPaper.setScoreRank(Constants.STU_EXAM_PAPER_INIT);
        stuExamPaper.setCount(count);
        stuExamPaper.setCreateDate(new Date());
        stuExamPaperMapper.insertSelective(stuExamPaper);

        // 添加 t_stu_exam_answer
        for (StuExamAnswer an : answers) {
            an.setExamPaperId(examPaperId);
            an.setStuId(stuId);
            stuExamAnswerMapper.insertSelective(an);
        }
    }

    // 如果处理时间长,可以通过额外线程执行
    @Override
    public void finishExam(Integer examPaperId) throws ExamException {
        ExamPaperExample examPaperExample = new ExamPaperExample();
        examPaperExample.or().andIdEqualTo(examPaperId);
        List<ExamPaper> list = examPaperMapper.selectByExample(examPaperExample);
        if(list.isEmpty()){
            throw new ExamException("不存在编号为["+examPaperId+"]的考试");
        }
        ExamPaper ep = list.get(0);
        switch (ep.getStatus()) {
            case Constants.EXAM_CREATED:
                throw new ExamException("考试还未开始, 不能结束");
            case Constants.EXAM_DOING:
                // 需要有参与考试的学生, 否则不能结束

                // 考试中, 可以结束考试
                break;
            case Constants.EXAM_FINISHED:
            case Constants.EXAM_RANKED:
                throw new ExamException("考试已结束");
            case Constants.EXAM_DELETED:
                throw new ExamException("该考试已被删除");
        }

        ExamPaperWithBLOBs examPaper = new ExamPaperWithBLOBs();
        examPaper.setId(examPaperId);
        examPaper.setStatus(Constants.EXAM_FINISHED);
        int update = examPaperMapper.updateByPrimaryKeySelective(examPaper);
        if(update < 1) {
            throw new ExamException("不存在编号为["+examPaperId+"]的考试");
        }

        // 自动批改选择题
        // 查询试卷中的选择题, 带正确答案
        List<QuestionWithBLOBs> qtList = questionMapper.selectQuestionsHasAnswerByExamPaperId(examPaperId, Constants.CHOICE_QUESTION_TYPE);

        // 一题一题的批改
        StuExamAnswerExample example = new StuExamAnswerExample();
        StuExamAnswer stuExamAnswer = new StuExamAnswer();
        for (QuestionWithBLOBs qt : qtList) {
            // 查询所有学生该试卷的对应的选择题
            example.clear();
            example.or()
                    .andExamPaperIdEqualTo(examPaperId)
                    .andQuestionIdEqualTo(qt.getId());
            List<StuExamAnswer> answers = stuExamAnswerMapper.selectByExampleWithBLOBs(example);
            for (StuExamAnswer an : answers) {
                // 获取学生的答案
                String answer = an.getAnswer();
                // 与正确的对比
                String[] arr = qt.getAnswer().split(",");
                int score = qt.getScore();
                if(arr.length > 1){
                    // 是多选题
                    for(String a: arr) {
                        if(!answer.contains(a)) {
                            // 学生的答案中 未包含 正确的答案, 则 算答错
                            score = 0;
                            break;
                        }
                    }
                } else {
                    // 是单选题
                    if(!qt.getAnswer().equals(answer)) {
                        score = 0;
                    }
                }
                if(score > 0) {
                    // 答对的, 修改分数
                    stuExamAnswer.setId(an.getId());
                    stuExamAnswer.setScore(score);
                    stuExamAnswerMapper.updateByPrimaryKeySelective(stuExamAnswer);
                }
            }
        }
    }

    @Override
    public void updateStuExamAnswerScore(Integer examPaperId, Integer stuId, List<StuExamAnswer> answers, Integer totalScore) throws ExamException {
        // 1.已统计过排名的考试, 不能修改得分
        ExamPaperWithBLOBs examPaper = examPaperMapper.selectByPrimaryKey(examPaperId);
        if(examPaper.getStatus().equals(Constants.EXAM_RANKED)) {
            throw new ExamException("已统计排名的考试不能修改得分");
        }
        if(!examPaper.getStatus().equals(Constants.EXAM_FINISHED)) {
            throw new ExamException("考试状态错误, 此时不能批改试卷");
        }

        // 2.StuExamAnswer 中必须要有 id, score, questionId
        final Map<Integer, StuExamAnswer> answerMap = new HashMap<>();
        answers.forEach(a -> answerMap.put(a.getQuestionId(), a));

        List<Integer> qtIdList = answers.stream().map(StuExamAnswer::getQuestionId).collect(Collectors.toList());

        // 查询题目, 验证 批改的 得分 是否超出限制
        QuestionExample example = new QuestionExample();
        example.or().andIdIn(qtIdList);
        List<Question> questionList = questionMapper.selectByExample(example);

        questionList.forEach( q -> {
            StuExamAnswer answer = answerMap.get(q.getId());
            if(answer != null) {
                // 得分 超出限制, 则默认设置 为 题目的 最大分值
                if(answer.getScore().compareTo(q.getScore()) > 0) {
                    answer.setScore(q.getScore());
                }
                // 更新 得分
                stuExamAnswerMapper.updateByPrimaryKeySelective(answer);
            }
        } );

        // 更新 学生的总得分
        StuExamPaper stuExamPaper = new StuExamPaper();
        stuExamPaper.setStuId(stuId);
        stuExamPaper.setExamPaperId(examPaperId);
        stuExamPaper.setTotalScore(totalScore);
        stuExamPaperMapper.updateByConditionSelective(stuExamPaper);
    }

    @Override
    public PageInfo<ExamPaper> findExamsByPage(int pageNum, int pageSize) throws ExamException {
        PageHelper.startPage(pageNum, pageSize);
        ExamPaperExample example = new ExamPaperExample();
        example.setOrderByClause("create_date desc");// 时间升序
        List<ExamPaper> list = examPaperMapper.selectByExample(example);
        return new PageInfo<ExamPaper>(list, 5);
    }

    @Override
    public PageInfo<StuExamPaper> findStuExamsByExamPaperIdAndPage(Integer examPaperId, int pageNum, int pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        StuExamPaperExample example = new StuExamPaperExample();
        example.or().andExamPaperIdEqualTo(examPaperId);
        example.setOrderByClause("total_score desc, create_date asc");// 总得分降序, 时间升序
        List<StuExamPaper> list = stuExamPaperMapper.selectByExample(example);
        // 关联学生信息
        list.forEach(sp -> {
            Stu stu = stuMapper.selectByPrimaryKey(sp.getStuId());
            sp.setStu(stu);
        });
        return new PageInfo<StuExamPaper>(list, 5);
    }

    @Override
    public void examRanking(Integer examPaperId) throws ExamException {
        ExamPaperExample examPaperExample = new ExamPaperExample();
        examPaperExample.or().andIdEqualTo(examPaperId);
        List<ExamPaper> list = examPaperMapper.selectByExample(examPaperExample);
        if(list.isEmpty()){
            throw new ExamException("不存在编号为["+examPaperId+"]的考试");
        }
        ExamPaper ep = list.get(0);
        switch (ep.getStatus()) {
            case Constants.EXAM_CREATED:
                throw new ExamException("考试还未开始");
            case Constants.EXAM_DOING:
                throw new ExamException("考试中");
            case Constants.EXAM_FINISHED:
                // 考试结束, 可以进行 排名, 但需要先手动批改完所有试卷
                break;
            case Constants.EXAM_RANKED:
                throw new ExamException("考试已结束");
            case Constants.EXAM_DELETED:
                throw new ExamException("该考试已被删除");
        }

        StuExamPaperExample stuExamPaperExample = new StuExamPaperExample();
        stuExamPaperExample.or()
                .andExamPaperIdEqualTo(examPaperId);
        stuExamPaperExample.setOrderByClause("total_score desc, create_date asc");// total_score 降序, create_date asc
        List<StuExamPaper> stuExamPapers = stuExamPaperMapper.selectByExample(stuExamPaperExample);
        int index = 1;
        int tempScore = 0;
        StuExamPaper updateStuExamPaper = new StuExamPaper();
        if(!stuExamPapers.isEmpty()) {
            StuExamPaper stuExamPaper = stuExamPapers.get(0);
            tempScore = stuExamPaper.getTotalScore();

            for (int i=0; i<stuExamPapers.size(); i++) {
                stuExamPaper = stuExamPapers.get(i);
                Integer totalScore = stuExamPaper.getTotalScore();
                if(tempScore > totalScore) {
                    tempScore = totalScore;
                    index++;
                }

                updateStuExamPaper.setId(stuExamPaper.getId());
                updateStuExamPaper.setScoreRank(index);
                stuExamPaperMapper.updateByConditionSelective(updateStuExamPaper);
            }

            // 更新 试卷 状态
            ExamPaperWithBLOBs examPaper = new ExamPaperWithBLOBs();
            examPaper.setId(examPaperId);
            examPaper.setStatus(Constants.EXAM_RANKED);// 已排名
            examPaperMapper.updateByPrimaryKeySelective(examPaper);
        }
    }

    @Override
    public void removeExam(Integer examPaperId) throws ExamException {

    }

    @Override
    public List<QuestionWithBLOBs> findExamQuestionsById(Integer examPaperId) {
        List<QuestionWithBLOBs> questions = questionMapper.selectQuestionsNoAnswerByExamPaperId(examPaperId, null);
        return questions;
    }

    @Override
    public PageInfo<QuestionWithBLOBs> findQuestionsByPage(int pageNum, int pageSize, Integer typeId, String language) {
        QuestionExample example = new QuestionExample();
        QuestionExample.Criteria or = example.or();
        if(null != typeId) {
            or.andTypeIdEqualTo(typeId);
        }
        if(null != language && !"".equals(language.trim())) {
            or.andLanguageEqualTo(language);
        }
        example.setOrderByClause(" type_id asc, create_date desc ");
        PageHelper.startPage(pageNum, pageSize);
        List<QuestionWithBLOBs> list = questionMapper.selectByExampleWithBLOBs(example);
        PageInfo<QuestionWithBLOBs> pageInfo = new PageInfo<>(list, 5);
        return pageInfo;
    }

    @Override
    public void addQuestionToExam(Integer examPaperId, List<Integer> questionIdList) throws ExamException {
        ExamPaperWithBLOBs examPaper = examPaperMapper.selectByPrimaryKey(examPaperId);
        if(null == examPaper) {
            throw new ExamException("此试卷不存在");
        }
        ExamPaperDetail detail = new ExamPaperDetail();
        detail.setExamPaperId(examPaperId);
        detail.setCreateDate(new Date());
        for(int qtId : questionIdList) {
            detail.setQuestionId(qtId);
            examPaperDetailMapper.insertSelective(detail);
        }
    }

    @Override
    public void removeQtFromExam(Integer examPaperId, List<Integer> questionIdList) throws ExamException {
        for(int qtId : questionIdList) {
            examPaperDetailMapper.deleteByExamPaperIdAndQuestionId(examPaperId, qtId);
        }
    }
}
